#pragma once
#include <stdio.h>
#include <iostream>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <exception>
#include <string.h>
#include <stdlib.h>
#include <clUtil.h>
#include <map>
#include <memory>
#include <algorithm>
#include <ctime>

#include "Scan.h"
#include "Database.h"
#include "init.h"

const unsigned int kScansPerIteration = 512;

struct ScorePair
{
  float score;
  unsigned int idx;

  bool operator < (const ScorePair& a) const
  {
    return score > a.score;
  }
};

class SpectrumException : public std::exception
{
  protected:
    char* errString;
  public:
    SpectrumException(const char* errString)
    {
      this->errString = (char*)errString;
    }
    virtual const char* what()
    {
      return this->errString;
    }
};

class Spectrum
{
  protected:
    std::string mSpecName;
    std::string mBaseName;
    std::vector<std::unique_ptr<Scan>> mMSScans;
    unsigned int mCurrentScan;
    size_t mMaxScanSize;
    std::unique_ptr<cl_uint[]> mAllPeaks;
    std::unique_ptr<cl_uint[]> mAllMZ;
    std::unique_ptr<MSScanInfo[]> mAllScanInfo;
    std::vector<std::vector<std::vector<ScorePair>>> mCombinedScores;
    unsigned int mTotalScans;
    void processRun(xmlNode* msRunNode);
    
    //ith entry in vector corresponds to ith device's buffer
    std::vector<std::unique_ptr<clUtil::Memory>> mDevicePeaks;
    std::vector<std::unique_ptr<clUtil::Memory>> mDevicePeaksTmp;
    std::vector<std::unique_ptr<clUtil::Memory>> mDeviceMZ;
    std::vector<std::unique_ptr<clUtil::Memory>> mDeviceMZTmp;
    std::vector<std::unique_ptr<clUtil::Memory>> mDeviceScanInfo;
    std::vector<std::unique_ptr<clUtil::Memory>> mDeviceClassThreshholds;
    std::vector<std::unique_ptr<clUtil::Memory>> mDeviceClassSizes;
    std::vector<std::unique_ptr<clUtil::Memory>> mDeviceFilteredMZ;
    std::vector<std::unique_ptr<clUtil::Memory>> mDeviceFilteredPeaks;
    std::vector<std::unique_ptr<clUtil::Memory>> mDeviceScores;
    std::vector<std::unique_ptr<clUtil::Memory>> mDeviceScoresIdx;
    std::vector<std::unique_ptr<clUtil::Memory>> mDevicePeptideMasses;
    std::vector<std::unique_ptr<clUtil::Memory>> mDevicePeptideDatabase;

    void allocateSpectrum(size_t currentDevice);

  public:
    Spectrum(const char* fileName);

    //Interesting functions
    void processSpectrum(Database& peptides);
    void combineScores(Database& peptides,const char* outputFileName);
    void allocateDevices();
    void freeDevices();

    void printScan(size_t id);
    void printAllScans();
  private:
    void writeHeader(FILE* outputFile);
};
